<?php

/**
 *
 * @desc Class for limiting running only one instance of some script
 * @see http://code.google.com/p/scriptalone
 * @author Barbushin Sergey http://www.linkedin.com/in/barbushin
 *
 */
class ScriptAlone_Process {

	/** @var ScriptAlone_Store */
	protected $store;

	/** @var ScriptAlone_State */
	protected $state;

	protected $withoutNotifyLifetime;
	protected $scriptLifetime;
	protected $stopOnReadyToStop;
	protected $scriptWasStopped;
	protected $scriptWasRestarted;
	protected $clearStateOnShutdown = true;

	public function __construct(ScriptAlone_Store $id, $withoutNotifyLifetime = null, $scriptLifetime = null, $stopOnReadyToStop = false) {
		$this->store = $id;
		$this->state = new ScriptAlone_State();
		$this->withoutNotifyLifetime = $withoutNotifyLifetime;
		$this->scriptLifetime = $scriptLifetime;
		$this->stopOnReadyToStop = $stopOnReadyToStop;

		register_shutdown_function(array($this, 'clearStateOnShutdown'));
		$this->notifyItWorks();
	}

	public function isReadyToStop() {
		return $this->scriptLifetime && ($this->state->started + $this->scriptLifetime - time()) < $this->withoutNotifyLifetime;
	}

	public function notifyItWorks() {
		$storedState = $this->store->getState();

		if($storedState) {
			if($storedState->pid != $this->state->pid && $storedState->isExpired()) {
				$this->stop('Another script instance still not expired', false);
			}
			elseif($storedState->status == 'stop') {
				$this->stop('Script process is stopped', false);
			}
			elseif($storedState->status == 'restart') {
				$this->stop('Script need to be restarted');
			}
		}
		elseif($this->state->isExpired()) {
			$this->stop('Process instance is expired');
		}
		if($this->stopOnReadyToStop && $this->isReadyToStop()) {
			$this->stop('Script lifetime is over');
		}

		$this->state->worked = time();
		$this->state->expire = time() + $this->withoutNotifyLifetime;

		set_time_limit($this->withoutNotifyLifetime);
		$this->store->saveState($this->state);
	}

	protected function setStoredStateStatus($status) {
		$state = $this->store->getState();
		if(!$state) {
			$state = new ScriptAlone_State();
		}
		$state->status = $status;
		$this->store->saveState($state);
	}

	public function processStop() {
		$this->setStoredStateStatus('stop');
	}

	public function processRestart() {
		$this->setStoredStateStatus('restart');
	}

	public function processResume() {
		$this->store->clearState();
	}

	protected function stop($message, $clearState = true) {
		$this->clearStateOnShutdown = $clearState;
		throw new ScriptAlone_Stopped($message);
	}

	public function clearStateOnShutdown() {
		if($this->clearStateOnShutdown) {
			$this->store->clearState();
		}
	}
}

class ScriptAlone_Stopped extends Exception {

}
